package com.jpragma.flexutils {
	import mx.collections.ArrayCollection;
	import mx.controls.dataGridClasses.DataGridColumn;

	/**
	 * Used as a drop-in replacement of DataGridColumn and supports nested fileds in dataField attribute.
	 * Supports sorting, considering data type of underlying datasource.
	 * @author Isaac Levin
	 */
	public class NetstedDataGridColumn extends DataGridColumn {
		public function NetstedDataGridColumn(columnName:String=null) {
			sortCompareFunction = sortCompare;
			super(columnName);
		}
		
		override public function itemToLabel(data:Object):String {
			if (data == null || dataField == null) {
				return super.itemToLabel(data);
			} else {
				if ((dataField.indexOf(".") == -1) && ((dataField.indexOf("[") == -1) || (dataField.indexOf("]") == -1))) {
						return super.itemToLabel(data);
				} else {
					var value:Object = getFieldValue(data);
					if (value == null) {
						return null;
					} else {
						return value.toString();
					}
				}
			}
		}
		
		public function getFieldValue(data:Object):Object {
			if (dataField.indexOf(".") == -1) {
				if ((dataField.indexOf("[") == -1) || (dataField.indexOf("]") == -1)) {
					return data[dataField];
				} else {
					// There is no nested elements, but there is an array
					var fldArray:Object = splitArrayFieldName(dataField);
					var fieldValue:Object = data[fldArray.fieldName];
					var elemValue:Object = getArrayElemValue(fieldValue, fldArray.idx);
					return elemValue;
				}
			} else {
				var currentData:Object = data;				
				var fields:Array = dataField.split(".");
				for each(var fld:String in fields) {
					if ((fld.indexOf("[") != -1) && (fld.indexOf("]") != -1)) {
						var fldArray:Object = splitArrayFieldName(fld);
						var fieldValue:Object = currentData[fldArray.fieldName];
						var elemValue:Object = getArrayElemValue(fieldValue, fldArray.idx);
						currentData = elemValue;												
					} else {
						currentData = currentData[fld];
					}
					if (currentData == null) {
						return null;
					}
				}
				return currentData;
			}			
		}
		
		private function splitArrayFieldName(fld:String):Object {
			var fieldName:String = fld.substring(0,fld.indexOf("["));
			var idx:int = parseInt(fld.substring(fld.indexOf("[")+1,fld.indexOf("]")));
			return {"fieldName":fieldName, "idx":idx};	
		} 
		
		private function getArrayElemValue(fieldValue:Object, idx:int):Object {
			var elemValue:Object;
			if (fieldValue is Array) {
				elemValue = fieldValue[idx];
			} else if (fieldValue is ArrayCollection) {
				elemValue = ArrayCollection(fieldValue).getItemAt(idx);
			} else {
				elemValue = fieldValue;
			}
			return elemValue;
		}
		
		private function sortCompare(leftData:Object, rightData:Object):int {
			var left:Object = getFieldValue(leftData);	
			var right:Object = getFieldValue(rightData);
			
			if (left == null || right == null) {
				if (left == null && right == null)
					return 0;
				return (left == null) ? 1 : -1;
			}			
			else if (left is int && right is int) {
				return compare(int(left) == int(right), int(left) > int(right));
			}
			else if (left is Number && right is Number) {
				return compare(Number(left) == Number(right), Number(left) > Number(right));
			}
			else if (left is String && right is String) {
				return compare(String(left) == String(right), String(left) > String(right));				
			}
			else if (left is Boolean && right is Boolean) {
				return compare(Boolean(left) == Boolean(right), Boolean(left) > Boolean(right));				
			}
			else if (left is Date && right is Date) {
				return compare((left as Date).time == (right as Date).time, (left as Date).time > (right as Date).time);
			}
			else {
				return sortCompare(left.toString(), right.toString());
			}
		}
		
		private static function compare(equal:Boolean, greaterThan:Boolean):int {
			if (equal)
				return 0;
			return greaterThan ? 1 : -1;
		}
	}
}